package com.rs.processor;

import com.rs.cache.LocalCache;
import com.rs.entity.basic.DeviceStatus;
import com.rs.entity.basic.MapDataPointVO;
import com.rs.entity.enums.NodeType;
import com.rs.entity.enums.VehicleAngle;
import com.rs.entity.enums.VehicleStatus;
import com.rs.entity.task.Target;
import com.rs.push.DevicePushMessage;
import com.rs.util.BackTurnUtil;
import com.rs.util.CoordinatorNodeUtil;
import com.rs.util.NodeUtil;
import com.rs.util.VehicleUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 命令处理器
 *
 * @author zhouwz
 * @date 2020/9/13 15:59
 */
@Slf4j
@Component
public class CommandProcessor {

    /**
     * 单车处理
     *
     * @param deviceStatus 车辆状态
     */
    @Async("asyncHandler")
    public void singleVehicleHandler(DeviceStatus deviceStatus) {
        String vehicleCode = deviceStatus.getDeviceNo();
        // 执行发送的指令
        commandPerform(vehicleCode);
        // 电量消耗
        electricConsumption(deviceStatus);
        // 上报当前车辆状态给RCS
        reportVehicleStatusToRcs(deviceStatus);
    }

    /**
     * 命令执行
     *
     * @param vehicleCode 车辆编号
     */
    private void commandPerform(String vehicleCode) {
        Target target = VehicleUtil.getTarget(vehicleCode);
        if (target == null) {
            return;
        }
        DeviceStatus currentStatus = VehicleUtil.getCurrentStatus(vehicleCode);
        // 还没到达最终目标不断递增
        BigDecimal xCoordinate = currentStatus.getXCoordinate();
        BigDecimal targetX = target.getTargetX();
        BigDecimal yCoordinate = currentStatus.getYCoordinate();
        BigDecimal targetY = target.getTargetY();
        // 根据角度判断行进的方向
        switch (VehicleAngle.getVehicleAngle(currentStatus.getAngle().intValue())) {
            case UP:
                if (BackTurnUtil.turnOnJudge(vehicleCode)) {
                    // 当前y坐标往下走没有到达终点就一直往下减偏移量
                    log.info("{} 倒车中,往下走", vehicleCode);
                    towardDown(vehicleCode, currentStatus, targetX, yCoordinate, targetY);
                } else {
                    // 当前y坐标往上走没有到达终点就一直往上加偏移量
                    towardUp(vehicleCode, currentStatus, targetX, yCoordinate, targetY);
                }
                break;
            case DOWN:
                if (BackTurnUtil.turnOnJudge(vehicleCode)) {
                    // 当前y坐标往上走没有到达终点就一直往上加偏移量
                    log.info("{} 倒车中,往上走", vehicleCode);
                    towardUp(vehicleCode, currentStatus, targetX, yCoordinate, targetY);
                } else {
                    // 当前y坐标往下走没有到达终点就一直往下减偏移量
                    towardDown(vehicleCode, currentStatus, targetX, yCoordinate, targetY);
                }
                break;
            case RIGHT:
                if (BackTurnUtil.turnOnJudge(vehicleCode)) {
                    // 当前X坐标往左走没有到达终点就一直往左减偏移量
                    log.info("{} 倒车中,往左走", vehicleCode);
                    towardLeft(vehicleCode, currentStatus, xCoordinate, targetX, targetY);
                } else {
                    // 当前X坐标往右走没有到达终点就一直往右加偏移量
                    towardRight(vehicleCode, currentStatus, xCoordinate, targetX, targetY);
                }
                break;
            case LEFT:
                if (BackTurnUtil.turnOnJudge(vehicleCode)) {
                    // 当前X坐标往右走没有到达终点就一直往右加偏移量
                    log.info("{} 倒车中,往右走", vehicleCode);
                    towardRight(vehicleCode, currentStatus, xCoordinate, targetX, targetY);
                } else {
                    // 当前X坐标往左走没有到达终点就一直往左减偏移量
                    towardLeft(vehicleCode, currentStatus, xCoordinate, targetX, targetY);
                }
                break;
            default:
        }
    }

    /**
     * 当前X坐标往左走没有到达终点就一直往左减偏移量
     *
     * @param vehicleCode   车辆编号
     * @param currentStatus 当前状态
     * @param xCoordinate   x坐标
     * @param targetX       目标x坐标
     * @param targetY       目标y坐标
     */
    private void towardLeft(String vehicleCode, DeviceStatus currentStatus, BigDecimal xCoordinate, BigDecimal targetX, BigDecimal targetY) {
        if (xCoordinate.compareTo(targetX) > 0) {
            currentStatus.setXCoordinate(currentStatus.getXCoordinate().subtract(LocalCache.MOVE_SCALE));
            log.info("{} 往左走,X [{}]| Y [{}]", vehicleCode, currentStatus.getXCoordinate().doubleValue(), currentStatus.getYCoordinate().doubleValue());
            throughLeftNodeReport(vehicleCode, currentStatus, targetY);
        } else {
            currentStatus.setXCoordinate(targetX);
            arriveHandler(vehicleCode, currentStatus, targetX, targetY);
        }
    }

    /**
     * 当前X坐标往右走没有到达终点就一直往右加偏移量
     *
     * @param vehicleCode   车辆编号
     * @param currentStatus 当前状态
     * @param xCoordinate   x坐标
     * @param targetX       目标x坐标
     * @param targetY       目标y坐标
     */
    private void towardRight(String vehicleCode, DeviceStatus currentStatus, BigDecimal xCoordinate, BigDecimal targetX, BigDecimal targetY) {
        if (xCoordinate.compareTo(targetX) < 0) {
            currentStatus.setXCoordinate(currentStatus.getXCoordinate().add(LocalCache.MOVE_SCALE));
            log.info("{} 往右走,X [{}]| Y [{}]", vehicleCode, currentStatus.getXCoordinate().doubleValue(), currentStatus.getYCoordinate().doubleValue());
            throughRightNodeReport(vehicleCode, currentStatus, targetY);
        } else {
            currentStatus.setXCoordinate(targetX);
            arriveHandler(vehicleCode, currentStatus, targetX, targetY);
        }
    }

    /**
     * 当前y坐标往下走没有到达终点就一直往下减偏移量
     *
     * @param vehicleCode   车辆编号
     * @param currentStatus 当前状态
     * @param targetX       目标x坐标
     * @param targetY       目标y坐标
     * @param yCoordinate   当前y坐标
     */
    private void towardDown(String vehicleCode, DeviceStatus currentStatus, BigDecimal targetX, BigDecimal yCoordinate, BigDecimal targetY) {
        if (yCoordinate.compareTo(targetY) > 0) {
            currentStatus.setYCoordinate(currentStatus.getYCoordinate().subtract(LocalCache.MOVE_SCALE));
            log.info("{} 往下走,X [{}]| Y [{}]", vehicleCode, currentStatus.getXCoordinate().doubleValue(), currentStatus.getYCoordinate().doubleValue());
            throughDownNodeReport(vehicleCode, currentStatus, targetX);
        } else {
            currentStatus.setYCoordinate(targetY);
            arriveHandler(vehicleCode, currentStatus, targetX, targetY);
        }
    }


    /**
     * 当前y坐标往上走没有到达终点就一直往上加偏移量
     *
     * @param vehicleCode   车辆编号
     * @param currentStatus 当前状态
     * @param targetX       目标x坐标
     * @param targetY       目标y坐标
     * @param yCoordinate   当前y坐标
     */
    private void towardUp(String vehicleCode, DeviceStatus currentStatus, BigDecimal targetX, BigDecimal yCoordinate, BigDecimal targetY) {
        if (yCoordinate.compareTo(targetY) < 0) {
            currentStatus.setYCoordinate(currentStatus.getYCoordinate().add(LocalCache.MOVE_SCALE));
            log.info("{} 往上走,X [{}]| Y [{}]", vehicleCode, currentStatus.getXCoordinate().doubleValue(), currentStatus.getYCoordinate().doubleValue());
            throughUpNodeReport(vehicleCode, currentStatus, targetX);
        } else {
            currentStatus.setYCoordinate(targetY);
            arriveHandler(vehicleCode, currentStatus, targetX, targetY);
        }
    }

    /**
     * 往上走点位进行上报
     *
     * @param vehicleCode   车辆编号
     * @param currentStatus 当前状态
     * @param targetX       目标x坐标
     */
    private void throughUpNodeReport(String vehicleCode, DeviceStatus currentStatus, BigDecimal targetX) {
        Set<BigDecimal> coordinatorSet = CoordinatorNodeUtil.processPoints.get(vehicleCode);
        if (coordinatorSet != null) {
            for (BigDecimal throughCoordinator : coordinatorSet) {
                // 实时状态小于等于对应点位后进行上报和删除操作
                if (throughCoordinator.compareTo(currentStatus.getYCoordinate()) <= 0) {
                    arriveProcessHandler(vehicleCode, currentStatus, targetX, throughCoordinator);
                    coordinatorSet.remove(throughCoordinator);
                    break;
                }
            }
        }
    }

    /**
     * 往下走点位进行上报
     *
     * @param vehicleCode   车辆编号
     * @param currentStatus 当前状态
     * @param targetX       目标x坐标
     */
    private void throughDownNodeReport(String vehicleCode, DeviceStatus currentStatus, BigDecimal targetX) {
        Set<BigDecimal> coordinatorSet = CoordinatorNodeUtil.processPoints.get(vehicleCode);
        if (coordinatorSet != null) {
            for (BigDecimal throughCoordinator : coordinatorSet) {
                // 实时状态大于等于对应点位后进行上报和删除操作
                if (throughCoordinator.compareTo(currentStatus.getYCoordinate()) >= 0) {
                    arriveProcessHandler(vehicleCode, currentStatus, targetX, throughCoordinator);
                    coordinatorSet.remove(throughCoordinator);
                    break;
                }
            }
        }
    }

    /**
     * 往左走点位进行上报
     *
     * @param vehicleCode   车辆编号
     * @param currentStatus 当前状态
     * @param targetY       目标Y坐标
     */
    private void throughLeftNodeReport(String vehicleCode, DeviceStatus currentStatus, BigDecimal targetY) {
        Set<BigDecimal> coordinatorSet = CoordinatorNodeUtil.processPoints.get(vehicleCode);
        if (coordinatorSet != null) {
            for (BigDecimal throughCoordinator : coordinatorSet) {
                // 实时状态大于等于对应点位Y坐标后进行上报和删除操作
                if (throughCoordinator.compareTo(currentStatus.getXCoordinate()) >= 0) {
                    arriveProcessHandler(vehicleCode, currentStatus, throughCoordinator, targetY);
                    coordinatorSet.remove(throughCoordinator);
                    break;
                }
            }
        }
    }

    /**
     * 往右走点位进行上报
     *
     * @param vehicleCode   车辆编号
     * @param currentStatus 当前状态
     * @param targetY       目标Y坐标
     */
    private void throughRightNodeReport(String vehicleCode, DeviceStatus currentStatus, BigDecimal targetY) {
        Set<BigDecimal> coordinatorSet = CoordinatorNodeUtil.processPoints.get(vehicleCode);
        if (coordinatorSet != null) {
            for (BigDecimal throughCoordinator : coordinatorSet) {
                // 实时状态小于等于对应点位Y坐标后进行上报和删除操作
                if (throughCoordinator.compareTo(currentStatus.getXCoordinate()) <= 0) {
                    arriveProcessHandler(vehicleCode, currentStatus, throughCoordinator, targetY);
                    coordinatorSet.remove(throughCoordinator);
                    break;
                }
            }
        }
    }

    /**
     * 到达点位处理
     *
     * @param vehicleCode   车辆编号
     * @param currentStatus 当前状态
     * @param targetX       目标X坐标
     * @param targetY       目标Y坐标
     */
    private void arriveHandler(String vehicleCode, DeviceStatus currentStatus, BigDecimal targetX, BigDecimal targetY) {
        CoordinatorNodeUtil.processPoints.put(vehicleCode, new TreeSet<>());
        currentStatus.setStatus(VehicleStatus.IDLE.getCode());
        arriveProcessHandler(vehicleCode, currentStatus, targetX, targetY);
    }


    /**
     * 到达过程点位处理
     *
     * @param vehicleCode   车辆编号
     * @param currentStatus 当前状态
     * @param targetX       目标X坐标
     * @param targetY       目标Y坐标
     */
    private void arriveProcessHandler(String vehicleCode, DeviceStatus currentStatus, BigDecimal targetX, BigDecimal targetY) {
        currentStatus.setNodeCode(CoordinatorNodeUtil.get(targetX, targetY).getNodeCode());
        currentStatus.setNodeName(CoordinatorNodeUtil.get(targetX, targetY).getNodeName());
        DevicePushMessage.nodeReport(vehicleCode, false);
    }

    /**
     * 电量消耗
     *
     * @param deviceStatus 车辆实时状态
     */
    private void electricConsumption(DeviceStatus deviceStatus) {
        MapDataPointVO node = NodeUtil.get(deviceStatus.getNodeName());
        BigDecimal power = deviceStatus.getPower();
        if (NodeType.CHARGE.getCode().equals(node.getNodeType())) {
            // 在充电点位则加电量并状态为充电中
            deviceStatus.setStatus(VehicleStatus.CHARGE.getCode());
            // 满电不充电
            if (power.compareTo(new BigDecimal(LocalCache.FULL_POWER)) < 0) {
                deviceStatus.setPower(power.add(LocalCache.CONSUMPTION_SCALE.multiply(new BigDecimal("3"))));
            }
        } else {
            // 空电不减电
            if (power.compareTo(new BigDecimal(1)) > 0) {
                deviceStatus.setPower(power.subtract(LocalCache.CONSUMPTION_SCALE));
            }
        }
    }

    /**
     * 上报当前车辆状态给RCS
     *
     * @param deviceStatus 车辆状态
     */
    private void reportVehicleStatusToRcs(DeviceStatus deviceStatus) {
        String vehicleCode = deviceStatus.getDeviceNo();
        DevicePushMessage.reportStatus2Rcs(vehicleCode);
        if (LocalCache.NODE_COUNT.get(vehicleCode) == null) {
            LocalCache.NODE_COUNT.put(vehicleCode, new AtomicInteger(0));
        } else {
            int count = LocalCache.NODE_COUNT.get(vehicleCode).getAndIncrement();
            if (count <= LocalCache.REPORT_NODE_LIMIT) {
                // 开启服务前几次往上报点位信息
                DevicePushMessage.nodeReport(vehicleCode, true);
            }
        }
    }
}
